package com.lanmon.base;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.metadata.ClassMetadata;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

/**
 * @desc
 * @author resigshy
 * @Date Apr 11, 2013 10:59:53 AM
 */
public class HibernateDao<T, PK extends Serializable> {
	
	protected Class<T> entityClass;
	
	@Autowired
	private SessionFactory sessionFactory;
	
	/**
	 * 获取当前的session
	 * 
	 * @return
	 */
	public Session getSession() {
		return sessionFactory.getCurrentSession();
	}
	
	@SuppressWarnings("unchecked")
	public HibernateDao() {
		Type type = this.getClass().getGenericSuperclass();
		String nameString = this.getClass().getSuperclass().getName();
		if (nameString.endsWith("HibernateDao")) {
			ParameterizedType type1 = (ParameterizedType) type;
			Type[] types = type1.getActualTypeArguments();
			entityClass = (Class<T>) types[0];
		}
		else {
			type = ((Class<T>) type).getGenericSuperclass();
			ParameterizedType type1 = (ParameterizedType) type;
			Type[] types = type1.getActualTypeArguments();
			entityClass = (Class<T>) types[0];
		}
	}
	
	public HibernateDao(final Class<T> entityClass) {
		this.entityClass = entityClass;
	}
	
	@SuppressWarnings("unchecked")
	public T loadObject(final PK id) {
		return (T) getSession().load(entityClass, id);
	}
	
	@SuppressWarnings("unchecked")
	public T getObject(final PK id) {
		return (T) getSession().get(entityClass, id);
	}
	
	/**
	 * 保存新增或修改的对象.<br>
	 * 主键==null 时才能 insert =="" 无效<br>
	 * 使用前先判断id是否为Blank，是设置id=null
	 */
	public void saveOrUpdateObject(final T entity) {
		getSession().saveOrUpdate(entity);
	}
	
	public void saveObject(final T entity) {
		getSession().save(entity);
	}
	
	public void updateObject(final T entity) {
		getSession().update(entity);
	}
	
	public void mergeObject(final T entity) {
		getSession().merge(entity);
	}
	
	/**
	 * 删除对象.
	 * 
	 * @param entity
	 *            对象必须是session中的对象或含id属性的transient对象.
	 */
	public void destroyObject(final T entity) {
		getSession().delete(entity);
	}
	
	/**
	 * 按id删除对象.<br>
	 * 先查出来，再删除,不推荐使用<br>
	 * 推荐执行hql删除
	 */
	public void destroyObject(final PK id) {
		destroyObject(loadObject(id));
	}
	
	/**
	 * 删除所有
	 * 
	 * @param entities
	 *            必须有id存在
	 */
	public void destroyAllObject(final Collection<?> entities) {
		if (entities == null)
			return;
		for (Object entity : entities) {
			getSession().delete(entity);
		}
	}
	
	public void destroyAllObject(final String ids) {
		if (StringUtils.isEmpty(ids))
			return;
		Query query = getSession().createQuery("delete from " + entityClass.getName() + " where " + getIdName() + " in (" + ids + ")");
		query.executeUpdate();
		query = null;
	}
	
	/**
	 * 获取全部对象.
	 */
	public List<T> findAllObjects() {
		return findObjects();
	}
	
	/**
	 * 获取全部对象,支持排序.
	 * 
	 * @param orderBy
	 * @param isAsc
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public List<T> findAllObjects(String orderBy, boolean isAsc) {
		Criteria c = createCriteria();
		if (isAsc) {
			c.addOrder(Order.asc(orderBy));
		}
		else {
			c.addOrder(Order.desc(orderBy));
		}
		return c.list();
	}
	
	/**
	 * 按属性查找对象列表,匹配方式为相等.
	 * 
	 * @param propertyName
	 * @param value
	 * @return
	 */
	public List<T> findObjectBy(final String propertyName, final Object value) {
		Criterion criterion = Restrictions.eq(propertyName, value);
		return findObjects(criterion);
	}
	
	/**
	 * 按属性查找唯一对象,匹配方式为相等.
	 * 
	 * @param propertyName
	 * @param value
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public T findUniqueObjectBy(final String propertyName, final Object value) {
		Criterion criterion = Restrictions.eq(propertyName, value);
		return (T) createCriteria(criterion).uniqueResult();
	}
	
	/**
	 * 按id列表获取对象.
	 * 
	 * @param ids
	 * @return
	 */
	public List<T> findObjectByIds(List<PK> ids) {
		return findObjects(Restrictions.in(getIdName(), ids));
	}
	
	/**
	 * 按HQL查询对象列表.
	 * 
	 * @param values
	 *            数量可变的参数,按顺序绑定.
	 * @param hql
	 * @param values
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public List<T> findObject(final String hql, final Object... values) {
		return createQuery(hql, values).list();
	}
	
	/**
	 * 按HQL查询对象列表.
	 * 
	 * @param values
	 *            命名参数,按名称绑定.
	 * @param hql
	 * @param values
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public List<T> findObject(final String hql, final Map<String, ?> values) {
		return createQuery(hql, values).list();
	}
	
	/**
	 * 按HQL查询唯一对象.
	 * 
	 * @param values
	 *            数量可变的参数,按顺序绑定.
	 * @param hql
	 * @param values
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public T findUniqueObject(final String hql, final Object... values) {
		return (T) createQuery(hql, values).uniqueResult();
	}
	
	/**
	 * 按HQL查询唯一对象.
	 * 
	 * @param values
	 *            命名参数,按名称绑定.
	 * @param hql
	 * @param values
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public T findUniqueObject(final String hql, final Map<String, ?> values) {
		return (T) createQuery(hql, values).uniqueResult();
	}
	
	/**
	 * 执行HQL进行批量修改/删除操作.
	 * 
	 * @param hql
	 * @param values
	 * @return
	 */
	public int executeUpdate(final String hql, final Object... values) {
		return createQuery(hql, values).executeUpdate();
	}
	
	/**
	 * 执行HQL进行修改删除
	 * 
	 * @return 更新记录数
	 * @param hql
	 * @param values
	 * @return
	 */
	public int executeUpdate(final String hql, final Map<String, ?> values) {
		return createQuery(hql, values).executeUpdate();
	}
	
	/**
	 * 根据查询HQL与参数列表创建Query对象.
	 * 
	 * 本类封装的find()函数全部默认返回对象类型为T,当不为T时使用本函数.
	 * 
	 * @param values
	 *            数量可变的参数,按顺序绑定.
	 */
	private Query createQuery(final String queryString, final Object... values) {
		Query query = getSession().createQuery(queryString);
		loadQueryParameter(query, values);
		return query;
	}
	
	/**
	 * 根据查询HQL与参数列表创建Query对象.
	 * 
	 * @param values
	 *            命名参数,按名称绑定.
	 * @param queryString
	 * @param values
	 * @return
	 */
	private Query createQuery(final String queryString, final Map<String, ?> values) {
		Query query = getSession().createQuery(queryString);
		if (values != null) {
			query.setProperties(values);
		}
		return query;
	}
	
	/**
	 * 按Criteria查询对象列表.
	 * 
	 * @param criterions
	 *            数量可变的Criterion.
	 * @param criterions
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public List<T> findObjects(final Criterion... criterions) {
		return createCriteria(criterions).list();
	}
	
	/**
	 * 按Criteria查询唯一对象.
	 * 
	 * @param criterions
	 *            数量可变的Criterion.
	 * @param criterions
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public T findUniqueObject(final Criterion... criterions) {
		return (T) createCriteria(criterions).uniqueResult();
	}
	
	/**
	 * 根据Criterion条件创建Criteria.
	 * 
	 * 本类封装的find()函数全部默认返回对象类型为T,当不为T时使用本函数.
	 * 
	 * @param criterions
	 *            数量可变的Criterion.
	 */
	private Criteria createCriteria(final Criterion... criterions) {
		Criteria criteria = getSession().createCriteria(entityClass);
		for (Criterion c : criterions) {
			criteria.add(c);
		}
		return criteria;
	}
	
	/**
	 * 初始化对象. 使用load()方法得到的仅是对象Proxy, 在传到View层前需要进行初始化.
	 * 只初始化entity的直接属性,但不会初始化延迟加载的关联集合和属性. 如需初始化关联属性,可实现新的函数,执行:
	 * Hibernate.initialize(user.getRoles())，初始化User的直接属性和关联集合.
	 * Hibernate.initialize
	 * (user.getDescription())，初始化User的直接属性和延迟加载的Description属性.
	 */
	public void initEntity(T entity) {
		Hibernate.initialize(entity);
	}
	
	/**
	 * 
	 * @param entityList
	 */
	public void initEntity(List<T> entityList) {
		for (T entity : entityList) {
			Hibernate.initialize(entity);
		}
	}
	
	/**
	 * 为Query添加distinct transformer.
	 * 
	 * @param query
	 * @return
	 */
	@SuppressWarnings("unused")
	private Query distinct(Query query) {
		query.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
		return query;
	}
	
	/**
	 * 为Criteria添加distinct transformer.
	 * 
	 * @param criteria
	 * @return
	 */
	@SuppressWarnings("unused")
	private Criteria distinct(Criteria criteria) {
		criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
		return criteria;
	}
	
	/**
	 * 取得对象的主键名.
	 * 
	 * @return
	 */
	private String getIdName() {
		ClassMetadata meta = sessionFactory.getClassMetadata(entityClass);
		return meta.getIdentifierPropertyName();
	}
	
	public void clear() {
		getSession().clear();
	}
	
	public void flush() {
		getSession().flush();
	}
	
	/**
	 * 执行update delete 的HQL
	 * 
	 * @param hql
	 */
	public void executeUpdateHql(final String hql, Object[] values) {
		Query query = getSession().createQuery(hql);
		loadQueryParameter(query, values);
		query.executeUpdate();
		query = null;
	}
	
	/**
	 * 执行update delete 的SQL
	 * 
	 * @param sql
	 */
	public void executeUpdateSql(final String sql, Object[] values) {
		SQLQuery query = getSession().createSQLQuery(sql);
		loadQueryParameter(query, values);
		query.executeUpdate();
		query = null;
	}
	
	@SuppressWarnings("unchecked")
	public List<T> findListHql(String hql, Object[] values) {
		Query query = createQueryHQL(hql);
		loadQueryParameter(query, values);
		List<T> list = query.list();
		query = null;
		hql = null;
		return list;
	}
	
	/**
	 * 数组集合
	 * 
	 * @param sql
	 * @param values
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	public List findListSql(String sql, Object[] values) {
		SQLQuery query = createQuerySQL(sql);
		loadQueryParameter(query, values);
		List list = query.list();
		query = null;
		sql = null;
		return list;
	}
	
	@SuppressWarnings("unchecked")
	public void findFetchPage(DetachedCriteria detachedCriteria, PageEntity<T> page) {
		Criteria criteria = detachedCriteria.getExecutableCriteria(getSession());
		// 总记录 获取后取消 投影
		Integer totalCount = countCriteriaResult(criteria);
		criteria.setProjection(null);
		loadPageParameter(criteria, page);
		page.setTotalPageCount(totalCount);
		page.setResult(criteria.list());
	}
	
	@SuppressWarnings("unchecked")
	public void findPageHql(String hql, Object[] values, PageEntity<T> page) {
		Query query = createQueryHQL(hql);
		loadQueryParameter(query, values);
		loadPageParameter(query, page);
		int totalCount = countQueryHQLResult(hql, values);
		page.setTotalPageCount(totalCount);
		page.setResult(query.list());
		hql = null;
		query = null;
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public void findPageSql(String sql, Object[] values, PageEntity page) {
		SQLQuery query = createQuerySQL(sql);
		loadQueryParameter(query, values);
		loadPageParameter(query, page);
		int totalCount = countQuerySQLResult(sql, values);
		page.setTotalPageCount(totalCount);
		page.setResult(query.list());
		query = null;
		sql = null;
	}
	
	@SuppressWarnings("unchecked")
	public List<T> findByExample(T t) throws Exception {
		Example example = Example.create(t);
		Criteria criteria = sessionFactory.openSession().createCriteria(entityClass);
		criteria.add(example);
		return criteria.list();
	}
	
	/**
	 * 查询结果条数
	 * 
	 * @param criteria
	 * @return
	 */
	private int countCriteriaResult(Criteria criteria) {
		return Integer.parseInt(criteria.setProjection(Projections.rowCount()).uniqueResult().toString());
	}
	
	private int countQueryHQLResult(String hql, Object[] values) {
		hql = "SELECT COUNT(1) FROM ( " + hql + " )";
		Query query = getSession().createQuery(hql);
		loadQueryParameter(query, values);
		int count = Integer.valueOf(query.uniqueResult().toString());
		query = null;
		hql = null;
		return count;
	}
	
	private int countQuerySQLResult(String sql, Object[] values) {
		sql = "SELECT COUNT(1) FROM ( " + sql + " )";
		SQLQuery query = getSession().createSQLQuery(sql);
		loadQueryParameter(query, values);
		int count = Integer.valueOf(query.uniqueResult().toString());
		query = null;
		sql = null;
		return count;
	}
	
	private Criteria createCriteria() {
		return getSession().createCriteria(entityClass);
	}
	
	private Query createQueryHQL(String hql) {
		return getSession().createQuery(hql);
	}
	
	private SQLQuery createQuerySQL(String sql) {
		return getSession().createSQLQuery(sql);
	}
	
	/**
	 * 装载 query 参数
	 * 
	 * @param query
	 * @param values
	 */
	private void loadQueryParameter(Query query, Object[] values) {
		if (values != null && values.length > 0) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
	}
	
	/**
	 * 装载分页条件
	 * 
	 * @param obj
	 * @param page
	 */
	private void loadPageParameter(Object obj, PageEntity<T> page) {
		if (obj instanceof Criteria) {
			Criteria criteria = (Criteria) obj;
			criteria.setFirstResult(page.getStart());
			criteria.setMaxResults(page.getLimit());
		}
		else if (obj instanceof Query) {
			Query query = (Query) obj;
			query.setFirstResult(page.getStart());
			query.setMaxResults(page.getLimit());
		}
	}
}
